package com.zzyl.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.base.ResponseResult;
import com.zzyl.constant.AccraditationRecordConstant;
import com.zzyl.constant.PendingTasksConstant;
import com.zzyl.constant.RetreatConstant;
import com.zzyl.dto.LeaveDto;
import com.zzyl.dto.RetreatClearingBillDto;
import com.zzyl.entity.*;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.*;
import com.zzyl.service.AccraditationRecordService;
import com.zzyl.service.ActFlowCommService;
import com.zzyl.service.CheckInConfigService;
import com.zzyl.service.LeaveService;
import com.zzyl.utils.CodeUtil;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.utils.StringUtils;
import com.zzyl.utils.UserThreadLocal;
import com.zzyl.vo.*;
import com.zzyl.vo.retreat.DueBack;
import com.zzyl.vo.retreat.RetreatBillVo;
import com.zzyl.vo.retreat.TasVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class LeaveServiceImpl implements LeaveService {

    private static final String LEAVE_CODE_PREFIX = "QJ";

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private AccraditationRecordMapper accraditationRecordMapper;

    @Autowired
    private ActFlowCommService actFlowCommService;

    @Autowired
    private AccraditationRecordService accraditationRecordService;

    @Autowired
    private LeaveMapper leaveMapper;

    @Autowired
    private ElderMapper elderMapper;

    @Autowired
    private CheckInConfigService checkInConfigService;


    /**
     * 请假申请
     * @param leave
     * @return
     */
    @Override
    public ResponseResult createLeave(Leave leave) {
        //1.验证状态
        Long elderId = Long.valueOf(leave.getElderId());
        Leave dbLeave = leaveMapper.selectByElderId(elderId);
        if (dbLeave != null && leave.getFlowStatus() == null) {
            return ResponseResult.error(dbLeave.getName() + "已经发起了申请请假");
        }

        if (ObjectUtil.isNotEmpty(leave.getTaskId())) {
            // 修改
            Leave oldRetreat = leaveMapper.selectByCode(leave.getLeaveNo());
            if (!oldRetreat.getElderId().equals(leave.getElderId())) {
                Elder elder = new Elder();
                elder.setId(Long.valueOf(leave.getElderId()));
                elder.setStatus(2);
                elderMapper.updateByPrimaryKeySelective(elder);
            }
        }

        //是否在入住期限内
//        CheckInConfig currentConfigByElderId = checkInConfigService.findCurrentConfigByElderId(Long.valueOf(leave.getElderId()));
//        if (currentConfigByElderId.getCostStartTime().isAfter(leave.getCreateTime()) || currentConfigByElderId.getCostEndTime().isBefore(leave.getCreateTime())) {
//            return ResponseResult.error("请在费用期限内发起请假申请");
//        }

        //从当前线程中获取用户数据
        String subject = UserThreadLocal.getSubject();
        User user = JSON.parseObject(subject, User.class);
        //申请人部门编号
        String deptNo = user.getDeptNo();

        //退住标题
        String title = leave.getName() + "的请假申请";
        //设置流程状态
        leave.setFlowStatus(Leave.FlowStatus.REVIEW.getCode());
        leave.setStatus(Leave.Status.APPLICATION.getCode());
        leave.setType(Leave.Type.APPLICATION.getCode());

        LocalDateTime time = leave.getActualReturnTime();
        LocalDateTime end = leave.getLeaveEnd();



        if(time != null){
            if(time.isAfter(end)){
                leave.setType(Leave.Type.FINISHED.getCode());
            }else{
                leave.setType(Leave.Type.CLOSED.getCode());
            }
        }

        if (leave.getLeaveNo() != null) {
            if (!user.getId().equals(leave.getApplicatId())) {
                return ResponseResult.error("不是申请人，不是能提交数据");
            }
            //申请人的名字
            leave.setTitle(title);
            //修改
            leaveMapper.update(leave);

        } else {
            leave.setApplicat(user.getRealName());
            leave.setApplicatId(user.getId());
            //创建时间
            leave.setCreateTime(LocalDateTime.now());
            leave.setTitle(title);
            leave.setCreateBy(user.getId());//创建人
            leave.setStatus(Retreat.Status.APPLICATION.getCode());
//            leave.setNursingName(elder);
            //设置下个操作人
            String retreatCode = CodeUtil.generateCode(LEAVE_CODE_PREFIX, redisTemplate, 5);
            leave.setLeaveNo(retreatCode);
//            leave.setDeptNo(deptNo);
            //设置下一个状态
            leaveMapper.createLeave(leave);

            //修改老人状态为请假....
            Elder elder = new Elder();
            elder.setId(Long.valueOf(leave.getElderId()));
            elder.setStatus(2);
            elderMapper.updateByPrimaryKeySelective(elder);
        }
        if (ObjectUtil.isNotEmpty(leave.getTaskId())) {
            actFlowCommService.completeProcess(leave.getTitle(), leave.getTaskId(),
                    user.getId().toString(), 1, leave.getStatus());
        }else {
            Map<String, Object> setvariables = setvariables(leave.getLeaveNo());
            actFlowCommService.start(Long.valueOf(leave.getId()), "leave", user, setvariables, true);
        }
        //保存审核（操作）记录
        Long nextAssignee = actFlowCommService.getNextAssignee("leave", "leave:" + leave.getId());
        RecoreVo recoreVo = getRecoreVo(
                leave,
                user,
                AccraditationRecordConstant.AUDIT_STATUS_PASS,
                "",
                "发起申请-申请请假",
                "护理组长审批-申请审批",
                nextAssignee,
                AccraditationRecordConstant.RECORD_HANDLE_TYPE_PROCESSED);
        accraditationRecordService.insert(recoreVo);

        return ResponseResult.success("提交成功");
    }

    /**
     * 获取操作记录数据
     * @param leave  请假对象
     * @param user  当前登录用户
     * @param status  审核状态
     * @param option  操作意见
     * @param step    当前步骤
     * @param nextStep   下一步说明
     * @param nextAssignee  下一个审核人
     * @return
     */
    private RecoreVo getRecoreVo(Leave leave, User user, Integer status,
                                 String option, String step, String nextStep,
                                 Long nextAssignee, Integer handleType){
        RecoreVo recoreVo = new RecoreVo();
        recoreVo.setId(Long.valueOf(leave.getId()));
        recoreVo.setType(AccraditationRecordConstant.RECORD_TYPE_LEAVE);
        recoreVo.setFlowStatus(leave.getFlowStatus());
        recoreVo.setStatus(status);
        recoreVo.setOption(option);
        recoreVo.setNextStep(nextStep);
        recoreVo.setNextAssignee(nextAssignee);
        recoreVo.setUserId(user.getId());
        recoreVo.setRealName(user.getRealName());
        recoreVo.setHandleType(handleType);
        recoreVo.setStep(step);
        return recoreVo;
    }

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private DeptMapper deptMapper;

    public Map<String, Object> setvariables(String id) {
        //设置流程变量
        Map<String,Object> variables = new HashMap<>();

        // 护理员
        Leave leave = leaveMapper.getLeaveByCode(id);
        Long applicatId = Long.valueOf(leave.getApplicatId());
        variables.put("assignee0", applicatId);

        variables.put("assignee0Name", leave.getApplicat());
        variables.put("processTitle", leave.getTitle());

        // 护理部部门编号
//        Dept dept = deptMapper.selectByDeptNo(RetreatConstant.NURSING_DEPT_CODE);
        List<Long> legatUserIds = userMapper.selectByDeptNo(RetreatConstant.NURSING_DEPT_CODE);
        //部门领导id
//        Long leaderId = dept.getLeaderId();
        variables.put("assignee1",legatUserIds.get(1));
        //请假天数
        variables.put("day",leave.getLeaveDays());

        if(leave.getLeaveDays() >= 3 || leave.getNursingLevelName().equals("特级护理等级")){
            // 护理部长
//            Dept jsDept = deptMapper.selectByDeptNo(RetreatConstant.NURSING_DEPT_CODE);
            //部门领导id
//            Long jsLeaderId = dept.getLeaderId();
            variables.put("assignee2", legatUserIds.get(0));
        }

        // 流程类型
        variables.put("processType", 2);

        // 流程类型
        variables.put("processCode", leave.getLeaveNo());

        // 流程类型
        variables.put("processStatus", 1);
        return variables;
    }

    @Override
    public ResponseResult<TasVo> getLeave(String leaveNo, String assigneeId, Integer flowStatus, String taskId) {
        //当前登录人id
        Long mgtUserId = UserThreadLocal.getMgtUserId();
        //需要返回到前端的数据
        TasVo vo = new TasVo();

        //查询请假单数据
        Leave leave = leaveMapper.getLeaveByCode(leaveNo);
        //审批记录数据
        List<AccraditationRecord> accraditationRecordList =
                accraditationRecordMapper.getAccraditationRecordByBuisId(leave.getId(),
                        PendingTasksConstant.TASK_TYPE_LEAVE);
        vo.setAccraditationRecords(accraditationRecordList);
        vo.setLeave(leave);
        //默认显示请假数据
        vo.setIsShow(1);

        //默认不显示撤回按钮
        vo.setIsRevocation(false);

        //当前请假单状态
        Integer dbFlowstatus = leave.getFlowStatus();

        //申请人撤回单独处理，如果前端传递过来的流程状态0(申请人状态),数据库目前的流程状态为1(护理组长待审批状态) 且 操作人是申请人  则显示撤回
        if (dbFlowstatus.equals(Leave.FlowStatus.REVIEW.getCode())
                && leave.getApplicatId().equals(mgtUserId)
                && flowStatus.equals(Leave.FlowStatus.APPLY.getCode())
                && leave.getStatus().equals(Leave.Status.APPLICATION.getCode())) {
            vo.setIsRevocation(true);
        }

        if (flowStatus <= 0 ) {
            flowStatus = leave.getFlowStatus();
        }

        Integer isShow = 1;

        if (ObjectUtil.isNotEmpty(taskId)) {
            isShow = actFlowCommService.isCurrentUserAndStep(taskId, flowStatus, leave);
        }

        //默认不显示撤回按钮
        vo.setIsRevocation(isShow == 2 && flowStatus.equals(leave.getFlowStatus() - 1) && leave.getStatus().equals(1));

        if (isShow.equals(2)) {
            isShow = 1;
        }
        if (isShow.equals(3)) {
            isShow = 1;
        }
        // 默认显示请假申请基本信息数据
        vo.setIsShow(isShow);

        vo.setType(2);
        //护理组主管审批完成之后，则不能撤回
        if (flowStatus.equals(Leave.FlowStatus.REVIEW.getCode())) {
            vo.setIsRevocation(false);
        }

        vo.setType(2);
        AccraditationRecord accraditationRecord = accraditationRecordMapper.getLastByBuisId(leave.getId(), PendingTasksConstant.TASK_TYPE_LEAVE);
        if (ObjectUtil.isNotEmpty(accraditationRecord)) {
            vo.setNextApprover(accraditationRecord.getNextApproverRole());
        }
        return ResponseResult.success(vo);
    }

    @Override
    public ResponseResult submitRetreat(LeaveDto leaveDto) {
        //从当前线程中获取用户数据
        String subject = UserThreadLocal.getSubject();
        UserVo user = JSON.parseObject(subject, UserVo.class);

        /**
         * 判断当前操作人与登录人是否一致
         */
        if (leaveDto.getAssigneeId() == null || !leaveDto.getAssigneeId().equals(user.getId().toString())) {
            return ResponseResult.error("当前任务不属于你");
        }

        //查询退住数据
        Leave leave = leaveMapper.getLeaveByCode(leaveDto.getCode());

        //判断老人是否已经完成了请假
        if (CheckIn.Status.FINISHED.getCode().equals(leave.getStatus())) {
            throw new BaseException("该老人已经完成请假办理");
        }

        //准备操作记录对象
        RecoreVo recoreVo = new RecoreVo();
        recoreVo.setRealName(user.getRealName());
        recoreVo.setUserId(user.getId());
        recoreVo.setStatus(AccraditationRecordConstant.AUDIT_STATUS_PASS);
        recoreVo.setId(Long.valueOf(leave.getId()));
        recoreVo.setType(AccraditationRecordConstant.RECORD_TYPE_LEAVE);
        recoreVo.setFlowStatus(leave.getFlowStatus());
        //审核状态为1，护理员组长审核
        if (leave.getFlowStatus().equals(Leave.FlowStatus.REVIEW.getCode())) {
            //更新请假单状态
            leaveMapper.updateLeaveByFlowStatus(leave.getId(), Leave.FlowStatus.APPROVAL.getCode());
            //操作记录
            recoreVo.setOption("同意");
            recoreVo.setStep("护理组长审批-申请审批");
//            if (leave.getLeaveDays() >= 3 || leave.getNursingLevelName().equals("特级护理等级")){
                recoreVo.setNextStep("护理部部长-请假审批");
//            }
            recoreVo.setHandleType(AccraditationRecordConstant.RECORD_HANDLE_TYPE_AUDIT);
        }

        //审核状态为2，护理部部长-请假审批

        if (leave.getLeaveDays() >= 3 || leave.getNursingLevelName().equals("特级护理等级")){

            if (leave.getFlowStatus().equals(Leave.FlowStatus.APPROVAL.getCode())) {
                //操作记录
                recoreVo.setOption("同意");
                recoreVo.setStep("护理部部长-请假审批");
                recoreVo.setNextStep("");
                recoreVo.setHandleType(AccraditationRecordConstant.RECORD_HANDLE_TYPE_PROCESSED);


                //修改退住单状态 status为为2(已完成)
                leave.setStatus(Leave.Status.FINISHED.getCode());
                leaveMapper.updateStatus(Long.valueOf(leave.getId()), Leave.Status.FINISHED.getCode());
            }
        }

        //老人状态改为请假
        Elder elder = new Elder();
        elder.setId(Long.valueOf(leave.getElderId()));
        elder.setStatus(2);
        elderMapper.updateByPrimaryKeySelective(elder);

        //完成任务
        actFlowCommService.completeProcess("", leaveDto.getTaskId(), user.getId().toString(), 1, leave.getStatus());

        //保存操作记录
        Long nextAssignee = actFlowCommService.getNextAssignee("leave", "leave:" + leave.getId());
        recoreVo.setNextAssignee(nextAssignee);
        accraditationRecordService.insert(recoreVo);

        return ResponseResult.success("提交成功");
    }

    /**
     * 获取操作步骤
     *
     * @return
     */
    private String getRecordCurrentStep(Integer flowStatus) {
        String currentStep = "";

        switch (flowStatus) {
            case 0:
                currentStep = "申请请假";
                break;
            case 1:
                currentStep = "申请审批";
                break;
            case 2:
                currentStep = "请假审批";
                break;
        }
        return currentStep;
    }

    /**
     * 审核
     * @param leaveNo  退住单code
     * @param reject   拒绝原因
     * @param taskId
     * @return
     */
    @Override
    public ResponseResult auditReject(String leaveNo, String reject, String taskId) {
        String subject = UserThreadLocal.getSubject();
        User user = JSON.parseObject(subject, User.class);

        //查询请假单
        Leave leave = leaveMapper.getLeaveByCode(leaveNo);
        if (leave == null) {
            throw new BaseException("请假单不存在");
        }

        //修改退住单状态为3（已关闭）
        leaveMapper.updateStatus(leave.getId(), Retreat.Status.CLOSED.getCode());

        //完成任务
        actFlowCommService.closeProcess(taskId, PendingTasksConstant.TASK_STATUS_CLOSED);
        //获取当前的审核步骤
        String step = getCurrentStep(leave.getFlowStatus());
        //保存审核记录
        RecoreVo recoreVo = getRecoreVo(leave,user,
                AccraditationRecordConstant.AUDIT_STATUS_REJECT,
                reject,
                step,"",null,AccraditationRecordConstant.RECORD_HANDLE_TYPE_AUDIT);
        accraditationRecordService.insert(recoreVo);

        //退住审核拒绝，则恢复老人状态
        Elder elder = new Elder();
        elder.setId(leave.getElderId());
        elder.setStatus(1);
        elderMapper.updateByPrimaryKeySelective(elder);

        return ResponseResult.success();
    }
    /**
     * 获取当前操作步骤
     * @param flowStatus
     * @return
     */
    private String getCurrentStep(Integer flowStatus) {
        String step = "";
        switch (flowStatus) {
            case 1:
                step = "护理组长-申请审批";
                break;
            case 2:
                step = "护理部长-请假审批";
                break;
        }
        return step;
    }

    /**
     * 撤回
     * @param leaveNo
     * @param flowStatus
     * @param taskId
     * @return
     */
    @Override
    public ResponseResult revocation(String leaveNo, Integer flowStatus, String taskId) {

        //获取登录用户
        String subject = UserThreadLocal.getSubject();
        User user = JSON.parseObject(subject, User.class);

        //查询请假单
        Leave leave = leaveMapper.getLeaveByCode(leaveNo);
        if (leave == null) {
            throw new BaseException("请假单不存在");
        }

        //当前状态+1 == reteat.flowstatus
        if (leave.getFlowStatus() > flowStatus) {
            throw new BaseException("请假单已审核，不能撤回");
        }

        //修改退住单  状态回退 -1
        Integer state = flowStatus - 1;
        leaveMapper.updateLeaveByFlowStatus(leave.getId(), state);
        //撤回任务
        actFlowCommService.withdrawTask(taskId, false);

        //保存审核记录
        String currentStep = "撤回处理";
        if (leave.getFlowStatus() == 1) {
            currentStep = "撤回处理";
        }
        if (leave.getFlowStatus() == 2) {
            currentStep = "撤回审批";
        }
        currentStep = currentStep + "-" + getRecordCurrentStep(leave.getFlowStatus() - 1);
        RecoreVo recoreVo = getRecoreVo(leave,user,
                AccraditationRecordConstant.AUDIT_STATUS_WITHDRAWS,"",
                currentStep,"",
                null,AccraditationRecordConstant.RECORD_HANDLE_TYPE_AUDIT);
        accraditationRecordService.insert(recoreVo);

        return ResponseResult.success();
    }

    /**
     * 查看请假列表
     * @param leaveNo        请假code
     * @param name           老人姓名
     * @param idCardNo       身份证号
     * @param start          开始时间
     * @param end            结束时间
     * @param pageNum        当前页
     * @param pageSize       每页显示条数
     * @param userId         用户id
     * @return
     */
    @Override
    public ResponseResult selectByPage(String leaveNo, String name, String idCardNo,
                                       LocalDateTime start, LocalDateTime end, Integer pageNum, Integer pageSize, Long userId) {
        PageHelper.startPage(pageNum, pageSize);
        Page<List<Leave>> lists = leaveMapper.selectByPage(leaveNo, name, idCardNo, start, end, userId);
        return ResponseResult.success(PageResponse.of(lists, Leave.class));
    }

    /**
     * 驳回
     * @param leaveNo
     * @param message
     * @param taskId
     * @return
     */
    @Override
    public ResponseResult disapprove(String leaveNo, String message, String taskId) {
        //获取登录用户
        String subject = UserThreadLocal.getSubject();
        User user = JSON.parseObject(subject, User.class);

        //查询请假单
        Leave leave = leaveMapper.getLeaveByCode(leaveNo);
        if (leave == null) {
            throw new BaseException("请假单不存在");
        }

        //修改退住状态为0
        leaveMapper.updateLeaveByFlowStatus(leave.getId(), Leave.FlowStatus.APPLY.getCode());

        actFlowCommService.rollBackTask(taskId, true);

        //保存审核记录
        RecoreVo recoreVo = getRecoreVo(leave,user,
                AccraditationRecordConstant.AUDIT_STATUS_DISAPPROVE,message,
                "驳回申请-" + getRecordCurrentStep(leave.getFlowStatus()),"",
                null,AccraditationRecordConstant.RECORD_HANDLE_TYPE_AUDIT);
        accraditationRecordService.insert(recoreVo);

        return ResponseResult.success();
    }


    /**
     * 撤销
     * @param leaveNo   请假编码
     * @param taskId
     * @return
     */
    @Override
    public ResponseResult cancel(String leaveNo, String taskId) {
        String subject = UserThreadLocal.getSubject();
        User user = JSON.parseObject(subject, User.class);

        //查询请假单
        Leave leave = leaveMapper.getLeaveByCode(leaveNo);
        if (leave == null) {
            throw new BaseException("请假单不存在");
        }

        leaveMapper.updateLeaveByFlowStatus(leave.getId(), leave.getFlowStatus() -1);
        //修改请假单状态为3（已关闭）
        leaveMapper.updateStatus(leave.getId(), Leave.Status.CLOSED.getCode());

        //申请人撤销操作，则恢复老人状态
        Elder elder = new Elder();
        elder.setId(leave.getElderId());
        elder.setStatus(1);
        elderMapper.updateByPrimaryKeySelective(elder);

        actFlowCommService.closeProcess(taskId, PendingTasksConstant.TASK_STATUS_CLOSED);

        //保存审核记录
        RecoreVo recoreVo = getRecoreVo(leave,user,
                AccraditationRecordConstant.AUDIT_STATUS_DISAPPROVE,"",
                "撤销申请-申请请假","",
                null,AccraditationRecordConstant.RECORD_HANDLE_TYPE_PROCESSED);
        accraditationRecordService.insert(recoreVo);

        return ResponseResult.success();
    }
}
